#ifndef  _IDTEST_
#define  _IDTEST_
//namespace myidspace
//{

  
#include <boost/foreach.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>

#include <iostream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <cstring>

  ////////////////////////

  //////////////////////////
using std::vector;
using std::cout;
using std::endl;
using std::string;
using std::setw;

  //  using namespace std;
  //////////////////////////
using namespace boost;
  // Index stuff
  using boost::multi_index_container;
  using namespace boost::multi_index;
extern int printw;
extern   string strSurfWhere[];
extern   string strPhWhere[];
extern   string strApWhere[];
  typedef enum{
    IN_BAR,IN_BULGE,IN_RING, IN_UNKNOWN
      } TWhereIsPhaseSpace;

typedef enum{AP_CASE, SURF_CASE, PH_CASE}TWhich;

  struct id_locator{
    int indexF;//index in the file
    int ID;//original ID of the particle
    TWhereIsPhaseSpace IN_PH;
    bool IN_SURF;
    bool IN_AP;
    ///////////////////
    // Constructor
    id_locator(int indF,int ID_):indexF(indF), ID(ID_)
    {
      IN_AP=(rand()%2)&& 1;
      IN_SURF=(rand()%2)&&1;
      IN_PH=(TWhereIsPhaseSpace)(rand()%4);}
    ///////////////////
    id_locator(int indF,int ID_, 
	       bool IN_SURF_, 
	       bool IN_AP_, 
	       TWhereIsPhaseSpace IN_PH_=IN_UNKNOWN ):indexF(indF), 
	 ID(ID_),
	 IN_PH(IN_PH_){IN_SURF=IN_SURF_;IN_AP=IN_AP_;}
     id_locator operator=(id_locator e){
       indexF=e.indexF;ID=e.ID;
       IN_PH=e.IN_PH;
       IN_SURF=e.IN_SURF;
       IN_AP=e.IN_AP;
       return *this;
       }
    friend std::ostream& operator<<(std::ostream& os,const id_locator& l)
    {	
      os<<l.indexF<<" "<<setw(printw)<<l.ID<<" "
	<<" "<<setw(printw)<<strSurfWhere[(int)l.IN_SURF].c_str()<<" "
	<<setw(printw)<<strPhWhere[(int)l.IN_PH].c_str()<<" "
	<< setw(printw)<<strApWhere[(int)l.IN_AP].c_str()<<std::endl;
      return os;
    }
  };
  ///// Index Accessor
  struct indexF{};// Index of particle in the file
  struct ID{};// ID of particle
  struct indS{};// In/OUT Surface 
  struct indA{};// Ap <0.5 or <1 Apocenter/Pericenter
  struct indP{};// Phase space position
  ////////////////////
  typedef multi_index_container<
    id_locator,
    indexed_by<
    ordered_unique<
    tag<indexF>,  BOOST_MULTI_INDEX_MEMBER(id_locator,int,indexF)>,
    ordered_unique<
    tag<ID>,BOOST_MULTI_INDEX_MEMBER(id_locator,int,ID)>,
    ordered_non_unique<
    tag<indS>, BOOST_MULTI_INDEX_MEMBER(id_locator,bool,IN_SURF)>, 
    ordered_non_unique<
    tag<indA>, BOOST_MULTI_INDEX_MEMBER(id_locator,bool,IN_AP)>, 
    ordered_non_unique<
    tag<indP>, BOOST_MULTI_INDEX_MEMBER(id_locator,TWhereIsPhaseSpace,IN_PH)> >
    > locator_set;


  template<typename Tag,typename MultiIndexContainer>
    void print_out_by(
		      const MultiIndexContainer& s,
		      std::string logname="t.log", bool verbose=true,
		      Tag* =0 // fixes a MSVC++ 6.0 bug with implicit template function parms 		      		      
		      )
    {
      // obtain a reference to the index tagged by Tag 

      const typename boost::multi_index::index<MultiIndexContainer,Tag>::type& i=
	get<Tag>(s);

      typedef typename MultiIndexContainer::value_type value_type;

      // dump the elements of the index to cout 
      if(verbose)
	std::copy(i.begin(),i.end(),std::ostream_iterator<value_type>(std::cout));
      std::ofstream file(logname.c_str());
      std::copy(i.begin(),i.end(),std::ostream_iterator<value_type>(file));
      file.close();

    }



  class CIdTest{
    
  public:
    CIdTest(){ DoIt();}; 
    ~CIdTest(){ save();};
    void save()
      {
	print_out_by<indexF>(loc_set, "ID_relation.log", false);
	
      }
    void DoIt()
      {
	// some init will be usefull
      };
    void  TestIt(void)
      {
	iVec.resize(10);

	int counter=0;
	BOOST_FOREACH(int  &i, iVec )
	  i=counter++;
	int i;
	BOOST_FOREACH(i, iVec )
	  {
	    loc_set.insert(id_locator(i,std::rand()));
	    std::cout<<i<<std::endl;
	  }
	cout<<"sorted by ID"<<endl;
	print_out_by<ID>(loc_set);
	cout<<"sorted by index"<<endl;
	print_out_by<indexF>(loc_set);
	cout<<"sorted by inSurface"<<endl;	
	print_out_by<indS>(loc_set);
	cout<<"sorted by inAp"<<endl;	
	print_out_by<indA>(loc_set);
  

      };
    bool insert(id_locator point)
      {
        locator_set::nth_index<1>::type& sorted_index=loc_set.get<1>();
	locator_set::nth_index_iterator<1>::type it=
	  sorted_index.find(point.ID);
	if(it == sorted_index.end())
	  {
	    loc_set.insert(point);
	  }
	else
	  {
	    sorted_index.replace(it,point);
	    return false;
	  }
	return true;
      }
    bool modify(id_locator point, TWhich which)
      {
        locator_set::nth_index<1>::type& sorted_index=loc_set.get<1>();
	locator_set::nth_index_iterator<1>::type it=
	  sorted_index.find(point.ID);
	if(it == sorted_index.end())
	  {	
	    loc_set.insert(point);
	  }
	else
	  {
	    id_locator tpoint=(*it);
	    switch(which)
	      {
	      case AP_CASE:
		tpoint.IN_AP=point.IN_AP;
		break;
	      case SURF_CASE:
		tpoint.IN_SURF=point.IN_SURF;
		break;	
	      case PH_CASE:
		tpoint.IN_PH=point.IN_PH;			    
	      };
	 
	    sorted_index.replace(it,tpoint);
	 
	    return true;
	  }
	return true;
      }



    size_t size(){
      cout<<"SIZE="<<loc_set.size()<<endl;
      return loc_set.size();
    }

  private:
    std::vector<int> iVec;
    locator_set loc_set;    
  };
  
//}
#endif
